﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Sixeyed.CodeGeneration.Metadata.Interfaces;
using reflection = System.Reflection;

namespace Sixeyed.CodeGeneration.Metadata
{
    /// <summary>
    /// Configuration for metadata source assemblies
    /// </summary>
    /// <remarks>
    /// Allows consumers to specify additional metadata sources to be loaded at
    /// runtime, in addition to sources provided in Sixeyed.CodeGeneration assembly
    /// </remarks>
    public class SourceAssemblyConfiguration
    {
        /// <summary>
        /// Gets/sets the list of assembly names to search for IMetadataSource classes
        /// </summary>
        public string AssemblyList { get; set; }

        /// <summary>
        /// Returns a list of all metadata sources
        /// </summary>
        /// <returns>Metadata source list</returns>
        public List<IMetadataSource> GetSources()
        {
            List<IMetadataSource> sources = new List<IMetadataSource>();
            List<IMetadataProvider> providers = new List<IMetadataProvider>();
            //add sources from the current assembly:
            AddAssemblyTypes(reflection.Assembly.GetExecutingAssembly(), sources, providers);
            //and any specified assemblies:
            if (!string.IsNullOrEmpty(this.AssemblyList))
            {
                foreach (string assemblyName in this.AssemblyList.Split(';'))
                {
                    try
                    {
                        reflection.Assembly sourceAssembly = reflection.Assembly.Load(assemblyName);
                        AddAssemblyTypes(sourceAssembly, sources, providers);
                    }
                    catch (Exception ex)
                    {
                        //swallow exception & log:
                        Debug.WriteLine(string.Format("SourceAssemblyConfiguration.GetSources - exception loading assembly {0}: {1}", assemblyName, ex.Message));
                    }
                }
            }
            //load each source's providers:
            foreach (IMetadataSource source in sources)
            {
                source.LoadProviders(providers);
            }
            return sources;
        }

        /// <summary>
        /// Adds IMetadataSource objects from the given assembly to the source collection
        /// </summary>
        /// <param name="sourceAssembly">Assembly to load</param>
        /// <param name="sources">Source collection to add to</param>
        /// <param name="providers">Provider collection to add to</param>
        private static void AddAssemblyTypes(reflection.Assembly sourceAssembly, List<IMetadataSource> sources, List<IMetadataProvider> providers)
        {
            foreach (Type type in sourceAssembly.GetTypes())
            {
                if (type.IsClass && !type.IsInterface && !type.IsAbstract)
                {
                    if (type.GetInterface("IMetadataSource") != null)
                    {
                        try
                        {
                            sources.Add(Activator.CreateInstance(type) as IMetadataSource);
                        }
                        catch (Exception ex)
                        {
                            //swallow exception & log:
                            Debug.WriteLine(string.Format("SourceAssemblyConfiguration.AddAssemblyTypes - exception creating instance of IMetadataSource class {0}: {1}", type.Name, ex.Message));
                        }
                    }
                    else if (type.GetInterface("IMetadataProvider") != null)
                    {
                        try
                        {
                            providers.Add(Activator.CreateInstance(type) as IMetadataProvider);
                        }
                        catch (Exception ex)
                        {
                            //swallow exception & log:
                            Debug.WriteLine(string.Format("SourceAssemblyConfiguration.AddAssemblyTypes - exception creating instance of IMetadataProvider class {0}: {1}", type.Name, ex.Message));
                        }
                    }
                }
            }
        }
    }
}
